﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.IO;
using System.Xml.Serialization;

using Extensibility;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.OneNote;

using OnPowerShapeAddin.Operations;
using OnPowerShapeAddin.ObjectModel.OneNote2010XMLSchema;

namespace OnPowerShapeAddin
{
    [Guid("A30DAA85-2EB9-408D-9243-9271EBCC4245")]
    [ProgId("OnPowerShape.Addin2010")]
    public class Addin : IDTExtensibility2, IRibbonExtensibility
    {
        private ApplicationClass onApp;
        private OneNoteHelper helper;

        public void OnAddInsUpdate(ref Array custom)
        {
            
        }

        public void OnBeginShutdown(ref Array custom)
        {
            if (onApp != null)
                onApp = null;
            if (helper != null)
                helper = null;
        }

        public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
        {
#if STARTDEBUGGER
            System.Diagnostics.Debugger.Launch();
#endif

            onApp = (ApplicationClass)Application;
            helper = new OneNoteHelper(onApp);
        }

        public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
        {
            onApp = null;
            helper = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        public void OnStartupComplete(ref Array custom)
        {
            
        }

        public string GetCustomUI(string RibbonID)
        {
            return Properties.Resources.ribbon;
        }

        public IStream GetImage(string imageName)
        {
            MemoryStream mem = new MemoryStream();

            var bmp = (System.Drawing.Bitmap)Properties.Resources.ResourceManager.GetObject(imageName);
            bmp.Save(mem, System.Drawing.Imaging.ImageFormat.Png);

            return new CCOMStreamWrapper(mem);
        }

        private void ShowDialog(System.Windows.Forms.Form form, Window context)
        {
            // see: http://code.msdn.microsoft.com/office/CSOneNoteRibbonAddIn-c3547362
            if (context == null)
                context = onApp.Windows.CurrentWindow;

            Win32WindowOwner owner = new Win32WindowOwner(context.WindowHandle);

            form.ShowDialog(owner);

            form.Dispose();
            form = null;
            context = null;
            owner = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }

        public void showHello(IRibbonControl control)
        {
            string id = onApp.Windows.CurrentWindow.CurrentPageId;

            /*
            using (PageUpdateContext c = helper.CreateUpdateContext(id))
            {
                InkDrawing inkDraw = new InkDrawing();
                inkDraw.Item = CustomShapeAddinGui.InkConverter.TestRectangle(100, 100);

                inkDraw.Position = new Position();
                inkDraw.Position.x = 0;
                inkDraw.Position.y = 0;

                c.Page.Items = new PageObject[] { inkDraw };
            }
             * */
        }

        public void OnGroupSelectedShapes(IRibbonControl control)
        {
            string pageId = onApp.Windows.CurrentWindow.CurrentPageId;
            Page page = helper.GetPageContent(pageId, PageInfo.piBinaryDataSelection);

            // get all selected PageObjects but InsertedFile and MediaFile
            var selectedObjects = from obj in page.Items
                                  where obj.selected == "all" && !(obj is InsertedFile) && !(obj is MediaFile)
                                  select obj;

            if (selectedObjects == null || selectedObjects.Count() == 0)
            {
                // nothing to do here ...
                return;
            }

            // get the most top-left element, this will be the positon of the generated Image element
            // to ensure the group is visible we take the highest z position
            var smallstPoint = selectedObjects.Select(s => s.Position).Aggregate(SmallerPosition);
            var highestZ = selectedObjects.Select(s => s.Position.z).Max();
            // TODO: if shape was scaled we need to get the size element and calculate scaled size
            //       but what to do with other elements??


            // convert the xml of the selected elements to a base64 encoded string
            // this string will be saved in the metadata of the generated png file
            // If we want to ungroup the elements later we take the string from the
            // metadata convert it back to xml and update the onenote page with it
            Page ungroupElementsPage = new Page();
            ungroupElementsPage.Items = selectedObjects.ToArray();
            string selectedShapesStr = helper.SerializePage(ungroupElementsPage);
            // string ungroupDataBase64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(selectedShapesStr));

            // draws ink to a bitmap and returns the bitmap bytes
            GroupingOperation groupOp = new GroupingOperation();
            byte[] imgBytes = groupOp.GroupElements(selectedObjects, selectedShapesStr);

            if (imgBytes == null)
                return;

            // add the generated bmp to the one note page
            using (PageUpdateContext c = helper.CreateUpdateContext(pageId))
            {
                Image img = new Image();

                img.format = "png";
                img.selected = "all";

                img.Position = smallstPoint;
                img.Position.z = highestZ;

                img.Size = new Size();  // set size to (0, 0)

                img.Item = imgBytes;

                c.Page.Items = new PageObject[] { img };
            }

            // to mimic the group effect we delete the "original" shapes
            foreach (var obj in selectedObjects)
                helper.DeletePageContent(pageId, obj);
           
        }

        public void OnUngroupSelectedShapes(IRibbonControl control)
        {
            string pageId = onApp.Windows.CurrentWindow.CurrentPageId;
            Page page = helper.GetPageContent(pageId, PageInfo.piBinaryDataSelection);

            var selectedImage = (from img in page.Items
                                 where img.selected == "all"
                                       && img is Image
                                       && ((Image)img).format == "png"
                                       && ((Image)img).Item is byte[]
                                 select (Image)img).FirstOrDefault();

            if (selectedImage == null)
            {
                // nothing to do here ...
                return;
            }

            var imgPosition = selectedImage.Position;
            byte[] pngBytes = (byte[])selectedImage.Item;

            GroupingOperation op = new GroupingOperation();
            string ungroupedXml = op.UngroupElements(pngBytes);

            if (string.IsNullOrEmpty(ungroupedXml))
            {
                // this image has no ungroupeddata, so ...
                return;
            }

            Page ungroupedElements = helper.DeserializePageXml(ungroupedXml);

            var smallstPoint = ungroupedElements.Items.Select(s => s.Position).Aggregate(SmallerPosition);
            
            double offsetX = imgPosition.x - smallstPoint.x;
            double offsetY = imgPosition.y - smallstPoint.y;

            foreach (var item in ungroupedElements.Items)
            {
                item.objectID = null;

                TranslatePosition(item.Position, offsetX, offsetY);
            }

            using (PageUpdateContext c = helper.CreateUpdateContext(pageId))
            {
                c.Page.Items = ungroupedElements.Items;
            }
            
            helper.DeletePageContent(pageId, selectedImage);
        }

        public void OnAddSelectedShapesToLib(IRibbonControl control)
        {
            
        }

        public void OnShowShapeLibrary(IRibbonControl control)
        {
            GUI.Form1 form = new GUI.Form1();

            ShowDialog(form, control.Context);
        }

        public bool CanShowShapeLibrary(IRibbonControl control)
        {
            return true;
        }

        public bool CanAddSelectedShapesToLib(IRibbonControl control)
        {
            return true;
        }

        public bool CanUngroupSelectedShapes(IRibbonControl control)
        {
            return true;
        }

        public bool CanGroupSelectedShapes(IRibbonControl control)
        {
            return true;
        }

        private static Position SmallerPosition(Position posA, Position posB)
        {
            Position result = new Position();

            result.x = Math.Min(posA.x, posB.x);
            result.y = Math.Min(posA.y, posB.y);

            return result;
        }

        private static void TranslatePosition(Position pos, double translateX, double translateY)
        {
            pos.x = Math.Round(pos.x + translateX, 5);
            pos.y = Math.Round(pos.y + translateY, 5);
        }
    }
}
